library(knitr)
library(ggplot2)
library(tools)
source("util_align.R")
source("util_de.R")

1 Introduction

To run all the simulations in this notebook, set runSimulation=TRUE To run the entire pipeline, set runPipeline=TRUE

runSimulation=FALSE
runPipeline=FALSE

2 Run the pipeline

This section automatically generates lists of differentially expressed genes after specifying user inputs. For the interpretation and validation of DE, see this section Before running the code, check the installation of required packages and add the path of each package to environment. If using Windows, Cygwin or Linux Subsystem for Windows 10 is required for the shell scripts (called by system function). Salmon is not supported on Windows, as of version 1.2.0

2.1 Required packages

The versions used while constructing this pipeline is listed in parentheses
FastQC(v0.11.8): http://www.bioinformatics.babraham.ac.uk/projects/fastqc/ Salmon(0.8.2):https://salmon.readthedocs.io/en/latest/index.html (Patro et al. 2017) R packages:

  • limma 3.32.10 (Ritchie et al. 2015)
  • biomaRt 2.32.1

2.2 Pipeline

2.2.1 User Input

Specify the following parameters before running the pipeline

outDir="Path to output directory"

## fastq files ##
dataDir_fq=c("vector of paths to directories containing all fastq files") #if there are multiple directories, each will be considered as a batch; the directory names will be used as batch name
fastq_extension=c('fastq','fq','fastq.gz','fq.gz')  #add fastq file extension if not already listed

## pseudo-alignment with salmon ##
salmon_index=NA #if using pre-computed index, replace NA with path
reference_fa=NA #if not using pre-computed index, replace NA with path to the fasta of reference transcriptome
reftxpt='ensembl' #name of reference
paired_end=TRUE #if library is paired-end
if(paired_end){
  #option 1: provide the min number of starting characters to identify a library pair, e.g. 9 for library01_left.fq, library01_right.fq, library02_left.fq, library01_right.fq
  libID_len=NA #int or vector of length len(dataDir_fq)
  #option 2: enter full path to all library pairs as shown
  libPairs=list(c('path to library 1 left reads','path to library 1 right reads'),c('library 2 left reads','library 2 right reads'))
}

## DE ##
condition<-list(covariate1=c("condition1_1","condition1_2"),covariate2=c("condition2_1","condition2_2")) #list the experimental conditions; e.g. list(sex=c('female',"male"),treatment=c("high","low"))
refcondition<-list(c("covariateName","conditionName"),c("covariateName","conditionName")) #condition with respect to which DE of other samples is calculated
### example of loading transcript to gene matrx from ensembl using biomaRt; alternatively you can provide a matrix with the first column (target_id) containing transcript id and second column (ens_gene) containing gene id; this step aggregates transcription-level alignment to gene-level counts ###
ensembl <- useMart("ENSEMBL_MART_ENSEMBL",dataset="mmusculus_gene_ensembl")
ttg<-biomaRt::getBM(attributes = c("ensembl_transcript_id","ensembl_gene_id"),mart = ensembl)
ttg <- dplyr::rename(ttg, target_id = ensembl_transcript_id,ens_gene = ensembl_gene_id)

Optional adjustments to default parameters

alignmethod<-"salmon" #name of the aligner

## salmon ##
kmer_length=31
libType='A'

## DE ##
lowcountfilter=TRUE #if filter out low-count genes by CPM threshold
threshCPM_byAvgColSum=1.5/10e6 #genes passing filter have averages of replicates > avgColsum*threshCPM_byAvgColSum; avgColSum = average of sums of counts of all samples; genes passing filter for each batch are calculated separately
maxNDEG<-'all' #maximum number of genes in the output matrix
FDR<-1 #maximum false discovery rate above which genes will not be included in output
multipletesting_method<-'BH' #method to adjust for multiple testing; choose from "none", "BH", "BY" and "holm"

Initializing analysis…

## checking output directory ##
if(!dir.exists(outDir)){dir.create(outDir)}

batches<-c()
for(fqi in dataDir_fq){
  fqi_name=basename(fqi)
  batches<-c(batches,fqi_name)
}
contrasts<-condition[-which(condition %in% c(refcondition))] #TODO: modify this to generate a list with length=length(refcondition)

2.2.2 QC with fastQC

for(fqi in dataDir_fq){
  fqi_name=basename(fqi)
  fastqc_out=file.path(outDir,"fastQC_results",fqi_name)
  dir.create(fastqc_out)
  
  for(fq in list_files_with_exts(fqi,fastq_extension,recursive=TRUE,full.names = TRUE)){
    system(paste0("fastqc -f fastq ",fq," --extract ","--outdir=",fastqc_out))
  }
}

2.2.3 Pseudo-alignment with salmon


setwd(outDir)
if(is.na(salmon_index)){
  system(paste0("salmon index -t ",reference_fa," -i transcripts_index_salmon --type quasi -k 31"))
  salmon_index="transcripts_index_salmon"
}

if(!paired_end){
  for(fqi in dataDir_fq){
    fqi_name=basename(fqi)
    salmon_out=file.path(outDir,paste0("salmon",reftxpt,fqi_name))
    dir.create(salmon_out)
    for(fq in list_files_with_exts(fqi,fastq_extension,recursive=TRUE,full.names = TRUE)){
      salmon_out_i=file.path(salmon_out,file_path_sans_ext(basename(fq)))
      dir.create(salmon_out_i)
      system(paste0("salmon quant -i ",salmon_index," -l ",libType," -1 ",fq," -o ",salmon_out_i))
    }
  }
}else{
  if(!is.na(libID_len)){
    getLibPairs(libID_len,dataDir_fq) #TODO: implement getLibPairs in util_align
  }
  for(fqpair in libPairs){
    fqpair_name=basename(dirname(fqpair[[1]]))
    salmon_out=file.path(outDir,paste0("salmon",reftxpt,fqpair_name))
    if(!dir.exists(salmon_out)){dir.create(salmon_out)}
    salmon_out_i=file.path(salmon_out,file_path_sans_ext(basename(fqpair[[1]])))
    dir.create(salmon_out_i)
    system(paste0("salmon quant -i ",salmon_index," -l ",libType," -1 ",fqpair[[1]]," -2 ",fqpair[[2]]," -o ",salmon_out_i))
  }
}

2.2.4 Differential expression analysis


#TODO: modify util_de.R, include multiple covariates & refcondition


sourcedir<-outDir
setwd(sourcedir)

##get counts
countsdf<-countdf(alignmethod,reftxpt,batches,condition)

##low count filtering##
if(lowcountfilter==TRUE){
  countsdfCPM<-filterCPM(countsdf,threshCPM_byAvgColSum)
}

savepath<-file.path(outDir,'de')
if(!dir.exists(savepath)){dir.create(savepath)}

for(c in contrasts){
  deresults<-de(countsdfCPM_photo2_no20mW,c,multipletesting_method,nDEG = maxNDEG,FDR = FDR)
  
  # drawvenn(deresults,"/Users/Xinyi/rna-seq/data/n2a_prelim/de/venn_de",refcondition,c)
  genelist(deresults,savepath,paste0(c))
}

3 Benchmark of pipelne & alternative choicees

3.1 (Pseudo-)alignment and Quantification

The first step in the analysis is usually to transform the fastq files into gene expression matrices. This involves aligning reads to reference genome or reference transcriptome and quantification of aligned reads. Here’s a list of choices to consider:

3.1.1 simulation and benchmark

We used Polyester (Frazee et al. 2015) to simulate RNAseq libraries where a user generated fold-change matrix is used as the ground truth. This simulation is based on mouse mRNA reference transcriptome from UCSC (simAlign/reftxmrna_ucsc_refgene.fasta.gz, assembly GRCm38/mm10, UCSC RefSeq (refGene) table (Karolchik et al. 2004)).

To use our simulated data, please refer to simAlign/simfasta1, simAlign/simfasta2, simAlign/simfasta3

To use our framework directly, please refer to the code below and replace parameter values as needed (set eval or runSimulation to TRUE before running).

3.1.1.1 Run simulation

Required packages: - Polyester (Frazee et al. 2015) - seqinr - Biostrings

refPath<-"reftx/reftxmrna_ucsc_refgene.fasta.gz" #path to reference transcriptome
ngroup<-2 #number of conditions in the library
ndata<-3 #number of replicates in each condition
deprob<-0.15 #probability of differential expression
demean<-2 #mean of fold change for DE genes
savepath_fcmtx='simfcmtx' #directory to save fold change matrix
savepath_lib="simfasta" #directory to save simulated libraries
simulateLib_polyester(refPath,ngroup,ndata,deprob,savepath_fcmtx,savepath_lib)

3.1.1.2 Comparisons of mapping rates

Mapping rates between Salmon and kallisto are comparible when using ensemble and UCSC as the references.Mapping rates can be found as outputs of Salmon and Kallisto

3.1.1.3 Comparisons of quantification errors

percent error of transcript X = abs(estimated transcript count of X - actual count of X)/actual count of X * 100

3.1.1.4 Impacts of reference and (pseudo-)alignment methods on downstream DE analysis

We compared the impacts on two DE algorithms: DEseq2 and limma-voom. To reproduce our results, run deAgreement.R (results will be stored in simAlign/results/deagreement). This detects DE between two time points in our alpha-syn experiment (T2 and T7).

Two examples using DEseq2 and voom are shown below.

3.2 Normalization and DE analysis

Statistical inferences of differential gene expression (DE), including the normalization procedures, usually make assumptions about typical datasets’ characteristics, such as gene-count distribution and degree of DE (Seyednasrollah, Laiho, and Elo 2015, Dillies et al. (2013), Dillies et al. (2013)). It is important to understand how robust each normalization or DGE analysis method is when its assumptions are violated.

To test the performance given datasets with distinct features, we simulated

  • 23 datasets with 11 varying parameters
  • 11 empirically simulated datasets with 5 varying parameters

All simulations used Splatter (Zappia, Belinda, and Alicia 2017) with dropout rate equals to zero. Fixed parameters of the empirical simulations were derived from a dataset by Bottomly et al (Bottomly et al. 2011).

3.2.1 Simulation results

We tested the following combinations of normalization and DE analysis methods:

  • edgeR with
    • TPM
    • FPKM
    • Trimmed Mean of M-values (TMM), default normalization of edgeR
    • Relative Log Expression (RLE)
    • Upper Quartile (UQ)
    • RUVg + TMM
    • RUVg (remove unwanted variation) + RLE
    • RUVg + UQ
    • DEGES (DEG elimination strategy) + TMM
    • DEGES + RLE, DEGES + TbT
  • DEGES with
    • DESeq2
    • baySeq
    • edgeR
    • limma-voom
  • baySeq, DESeq2, and limma-voom with their default normalization methods

The benchmark results can be found in testdata/normtestresults and testdata/DEtestresults
The simulated datasets can be found in testdata/simulatedData
To reproduce our results, see the next section

Choice of normalization methods had very small impact on the benchmarking results. The ROC, accuracy vs FDR, and FDR control curves are comparable for all normalizations. An example is shown for ROC

Choice of DE analysis methods has trade-off between true positive rates and false discovery rates

3.2.2 Run simulation & benchmark

Requires: - splatter 1.0.3 (Zappia, Belinda, and Alicia 2017)

## enter the parameter values you want to test ##
groupCells<-c(3,9,6)
mean.shape<-c(0.4,0.8,0.6)
mean.rate<-c(0.1,0.5,0.3)
lib.loc<- c(6,16,11)
lib.scale<-c(0.15,0.5,0.35)
out.prob<-c(0.01,0.1,0.05)
de.prob<-c(0.05,0.3,0.1)
de.downProb<-c(0.25,0.75,0.5)
de.facLoc<-c(0.05,0.3,0.1)
de.facScale<-c(0.2,0.6,0.4)
bcv.common<-c(0.05,0.2,0.1)

generateSimulatedCounts(normalizationMethod,deMethod,groupCells,mean.shape,mean.rate,lib.loc,lib.scale,out.prob,de.prob,de.downProb,de.facLoc,bcv.common)

4 Interpretation and validation of differential expression analysis

4.1 False positives

We performed RNA-seq twice on a total of 6 samples of mouse Neuro2A cell cultures (3 samples for each RNA-seq). The same protocols were used and all libraries have comparable quality. We found 2665 differentially expressed genes with FDR < 0.05 using the default settings of this pipeline. With the limited number of replicates available in a typical RNA-seq experiment, it is important to consider confounding differences caused by experimental design and handling. In the following sections, we provide some methods for validation and interpretation of DE genes.

4.2 Functional enrichment analysis

4.2.1 Statistical over-representation test

This tests for any over or under represented annotation terms in the query list with respect to the background. Several options are listed below:

  • DAVID (D. W. Huang, Sherman, and Lempicki 2009): https://david.ncifcrf.gov/
    • Only over-representations are calculated
    • Last knowledge base update was in May 2016
    • DAVID has access to multiple databases
  • PANTHER (Mi et al. 2019): http://pantherdb.org/
    • Both over and under representations are calculated
    • Maintained up to date with Gene Ontology annotations

4.2.1.1 Choosing background gene list

Some common choices of background include genes known to be expressed in a particular cell type of interest. A proxy for cell-type specific background can be found by using all non-zero genes:

countsmtx<-as.matrix(as.data.frame(countsdf))
background<-rownames(countsmtx)[which(rowSums(countsmtx) > 0)]

However, it is possible that this list does not cover all genes that can be expressed due to the limited experimental conditions tested.

4.2.2 GSEA

4.2.3 WGCNA

4.2.4 ADAGE

4.3 Dimensionality reduction

4.4 Enrichment of transcription factors

4.5 Batch consensus

5 References

Bottomly, Daniel, Nicole A. R. Walter, Jessica Ezzell Hunter, Priscila Darakjian, Sunita Kawane, Kari J. Buck, Robert P. Searles, Michael Mooney, Shannon K. McWeeney, and Robert Hitzemann. 2011. “Evaluating Gene Expression in C57BL/6J and DBA/2J Mouse Striatum Using RNA-Seq and Microarrays.” PloS One 6 (3): e17820. doi:10.1371/journal.pone.0017820.

Dillies, Marie-Agnès, Andrea Rau, Julie Aubert, Christelle Hennequet-Antier, Marine Jeanmougin, Nicolas Servant, Céline Keime, et al. 2013. “A Comprehensive Evaluation of Normalization Methods for Illumina High-Throughput RNA Sequencing Data Analysis.” Briefings in Bioinformatics 14 (6): 671–83. doi:10.1093/bib/bbs046.

Frazee, Alyssa C., Andrew E. Jaffe, Ben Langmead, and Jeffrey T. Leek. 2015. “Polyester: Simulating RNA-Seq Datasets with Differential Transcript Expression.” Bioinformatics 31 (17): 2778–84. doi:10.1093/bioinformatics/btv272.

Huang, Da Wei, Brad T. Sherman, and Richard A. Lempicki. 2009. “Systematic and Integrative Analysis of Large Gene Lists Using DAVID Bioinformatics Resources.” Nature Protocols 4 (1): 44–57. doi:10.1038/nprot.2008.211.

Karolchik, Donna, Angela S. Hinrichs, Terrence S. Furey, Krishna M. Roskin, Charles W. Sugnet, David Haussler, and W. James Kent. 2004. “The UCSC Table Browser Data Retrieval Tool.” Nucleic Acids Research 32 (Database issue): D493–496. doi:10.1093/nar/gkh103.

Mi, Huaiyu, Anushya Muruganujan, Xiaosong Huang, Dustin Ebert, Caitlin Mills, Xinyu Guo, and Paul D. Thomas. 2019. “Protocol Update for Large-Scale Genome and Gene Function Analysis with the PANTHER Classification System (V.14.0).” Nature Protocols 14 (3): 703–21. doi:10.1038/s41596-019-0128-8.

Patro, Rob, Geet Duggal, Michael I. Love, Rafael A. Irizarry, and Carl Kingsford. 2017. “Salmon Provides Fast and Bias-Aware Quantification of Transcript Expression.” Nature Methods 14 (4): 417. doi:10.1038/nmeth.4197.

Ritchie, Matthew E., Belinda Phipson, Di Wu, Yifang Hu, Charity W. Law, Wei Shi, and Gordon K. Smyth. 2015. “Limma Powers Differential Expression Analyses for RNA-Sequencing and Microarray Studies.” Nucleic Acids Research 43 (7): e47–e47. doi:10.1093/nar/gkv007.

Seyednasrollah, Fatemeh, Asta Laiho, and Laura L. Elo. 2015. “Comparison of Software Packages for Detecting Differential Expression in RNA-Seq Studies.” Briefings in Bioinformatics 16 (1): 59–70. doi:10.1093/bib/bbt086.

Zappia, Luke, Phipson Belinda, and Oshlack Alicia. 2017. “Splatter: Simulation of Single-Cell RNA Sequencing Data Genome Biology Full Text.” Genome Biology 18 (174). https://genomebiology.biomedcentral.com/articles/10.1186/s13059-017-1305-0.

Zhao, Shanrong, and Baohong Zhang. 2017. “A Comprehensive Evaluation of Ensembl, RefSeq, and UCSC Annotations in the Context of RNA-Seq Read Mapping and Gene Quantification BMC Genomics Full Text.” Accessed December 18. https://bmcgenomics.biomedcentral.com/articles/10.1186/s12864-015-1308-8.

LS0tDQp0aXRsZTogIlJOQXNlcSBkYXRhIHNpbXVsYXRpb24gYW5kIGJlbmNobWFyayINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIHRoZW1lOiBjb3Ntbw0KICAgIHRvYzogeWVzDQogICAgdG9jX2Zsb2F0OiB5ZXMNCiAgaHRtbF9ub3RlYm9vazoNCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIHRoZW1lOiBjb3Ntbw0KICAgIHRvYzogeWVzDQogICAgdG9jX2Zsb2F0OiB5ZXMNCmJpYmxpb2dyYXBoeTogcGlwZWxpbmUuYmliDQotLS0NCg0KYGBge3J9DQpsaWJyYXJ5KGtuaXRyKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeSh0b29scykNCnNvdXJjZSgidXRpbF9hbGlnbi5SIikNCnNvdXJjZSgidXRpbF9kZS5SIikNCmBgYA0KDQoNCiNJbnRyb2R1Y3Rpb24NCg0KVG8gcnVuIGFsbCB0aGUgc2ltdWxhdGlvbnMgaW4gdGhpcyBub3RlYm9vaywgc2V0IHJ1blNpbXVsYXRpb249VFJVRQ0KVG8gcnVuIHRoZSBlbnRpcmUgcGlwZWxpbmUsIHNldCBydW5QaXBlbGluZT1UUlVFDQpgYGB7cn0NCnJ1blNpbXVsYXRpb249RkFMU0UNCnJ1blBpcGVsaW5lPUZBTFNFDQpgYGANCg0KI1J1biB0aGUgcGlwZWxpbmUNClRoaXMgc2VjdGlvbiBhdXRvbWF0aWNhbGx5IGdlbmVyYXRlcyBsaXN0cyBvZiBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMgYWZ0ZXIgc3BlY2lmeWluZyBbdXNlciBpbnB1dHNdKCN1c2VySW5wdXRfcGlwZWxpbmUpLiBGb3IgdGhlIGludGVycHJldGF0aW9uIGFuZCB2YWxpZGF0aW9uIG9mIERFLCBzZWUgW3RoaXMgc2VjdGlvbl0oI2FmdGVyREUpDQpCZWZvcmUgcnVubmluZyB0aGUgY29kZSwgW2NoZWNrIHRoZSBpbnN0YWxsYXRpb24gb2YgcmVxdWlyZWQgcGFja2FnZXNdKCNydW5QaXBlbGluZV9wYWNrYWdlKSBhbmQgYWRkIHRoZSBwYXRoIG9mIGVhY2ggcGFja2FnZSB0byBlbnZpcm9ubWVudC4NCklmIHVzaW5nIFdpbmRvd3MsIEN5Z3dpbiBvciBMaW51eCBTdWJzeXN0ZW0gZm9yIFdpbmRvd3MgMTAgaXMgcmVxdWlyZWQgZm9yIHRoZSBzaGVsbCBzY3JpcHRzIChjYWxsZWQgYnkgc3lzdGVtIGZ1bmN0aW9uKS4NClNhbG1vbiBpcyBub3Qgc3VwcG9ydGVkIG9uIFdpbmRvd3MsIGFzIG9mIHZlcnNpb24gMS4yLjANCg0KIyNSZXF1aXJlZCBwYWNrYWdlcyB7I3J1blBpcGVsaW5lX3BhY2thZ2V9DQpUaGUgdmVyc2lvbnMgdXNlZCB3aGlsZSBjb25zdHJ1Y3RpbmcgdGhpcyBwaXBlbGluZSBpcyBsaXN0ZWQgaW4gcGFyZW50aGVzZXMgIA0KRmFzdFFDKHYwLjExLjgpOiBodHRwOi8vd3d3LmJpb2luZm9ybWF0aWNzLmJhYnJhaGFtLmFjLnVrL3Byb2plY3RzL2Zhc3RxYy8NClNhbG1vbigwLjguMik6aHR0cHM6Ly9zYWxtb24ucmVhZHRoZWRvY3MuaW8vZW4vbGF0ZXN0L2luZGV4Lmh0bWwgW0BwYXRyb19zYWxtb25fMjAxN10NClIgcGFja2FnZXM6DQoNCi0gbGltbWEgMy4zMi4xMCBbQHJpdGNoaWVfbGltbWFfMjAxNV0NCi0gYmlvbWFSdCAyLjMyLjENCg0KDQojI1BpcGVsaW5lDQojIyNVc2VyIElucHV0IHsjdXNlcklucHV0X3BpcGVsaW5lfQ0KU3BlY2lmeSB0aGUgZm9sbG93aW5nIHBhcmFtZXRlcnMgYmVmb3JlIHJ1bm5pbmcgdGhlIHBpcGVsaW5lDQpgYGB7ciwgZXZhbD1ydW5QaXBlbGluZX0NCm91dERpcj0iUGF0aCB0byBvdXRwdXQgZGlyZWN0b3J5Ig0KDQojIyBmYXN0cSBmaWxlcyAjIw0KZGF0YURpcl9mcT1jKCJ2ZWN0b3Igb2YgcGF0aHMgdG8gZGlyZWN0b3JpZXMgY29udGFpbmluZyBhbGwgZmFzdHEgZmlsZXMiKSAjaWYgdGhlcmUgYXJlIG11bHRpcGxlIGRpcmVjdG9yaWVzLCBlYWNoIHdpbGwgYmUgY29uc2lkZXJlZCBhcyBhIGJhdGNoOyB0aGUgZGlyZWN0b3J5IG5hbWVzIHdpbGwgYmUgdXNlZCBhcyBiYXRjaCBuYW1lDQpmYXN0cV9leHRlbnNpb249YygnZmFzdHEnLCdmcScsJ2Zhc3RxLmd6JywnZnEuZ3onKSAgI2FkZCBmYXN0cSBmaWxlIGV4dGVuc2lvbiBpZiBub3QgYWxyZWFkeSBsaXN0ZWQNCg0KIyMgcHNldWRvLWFsaWdubWVudCB3aXRoIHNhbG1vbiAjIw0Kc2FsbW9uX2luZGV4PU5BICNpZiB1c2luZyBwcmUtY29tcHV0ZWQgaW5kZXgsIHJlcGxhY2UgTkEgd2l0aCBwYXRoDQpyZWZlcmVuY2VfZmE9TkEgI2lmIG5vdCB1c2luZyBwcmUtY29tcHV0ZWQgaW5kZXgsIHJlcGxhY2UgTkEgd2l0aCBwYXRoIHRvIHRoZSBmYXN0YSBvZiByZWZlcmVuY2UgdHJhbnNjcmlwdG9tZQ0KcmVmdHhwdD0nZW5zZW1ibCcgI25hbWUgb2YgcmVmZXJlbmNlDQpwYWlyZWRfZW5kPVRSVUUgI2lmIGxpYnJhcnkgaXMgcGFpcmVkLWVuZA0KaWYocGFpcmVkX2VuZCl7DQogICNvcHRpb24gMTogcHJvdmlkZSB0aGUgbWluIG51bWJlciBvZiBzdGFydGluZyBjaGFyYWN0ZXJzIHRvIGlkZW50aWZ5IGEgbGlicmFyeSBwYWlyLCBlLmcuIDkgZm9yIGxpYnJhcnkwMV9sZWZ0LmZxLCBsaWJyYXJ5MDFfcmlnaHQuZnEsIGxpYnJhcnkwMl9sZWZ0LmZxLCBsaWJyYXJ5MDFfcmlnaHQuZnENCiAgbGliSURfbGVuPU5BICNpbnQgb3IgdmVjdG9yIG9mIGxlbmd0aCBsZW4oZGF0YURpcl9mcSkNCiAgI29wdGlvbiAyOiBlbnRlciBmdWxsIHBhdGggdG8gYWxsIGxpYnJhcnkgcGFpcnMgYXMgc2hvd24NCiAgbGliUGFpcnM9bGlzdChjKCdwYXRoIHRvIGxpYnJhcnkgMSBsZWZ0IHJlYWRzJywncGF0aCB0byBsaWJyYXJ5IDEgcmlnaHQgcmVhZHMnKSxjKCdsaWJyYXJ5IDIgbGVmdCByZWFkcycsJ2xpYnJhcnkgMiByaWdodCByZWFkcycpKQ0KfQ0KDQojIyBERSAjIw0KY29uZGl0aW9uPC1saXN0KGNvdmFyaWF0ZTE9YygiY29uZGl0aW9uMV8xIiwiY29uZGl0aW9uMV8yIiksY292YXJpYXRlMj1jKCJjb25kaXRpb24yXzEiLCJjb25kaXRpb24yXzIiKSkgI2xpc3QgdGhlIGV4cGVyaW1lbnRhbCBjb25kaXRpb25zOyBlLmcuIGxpc3Qoc2V4PWMoJ2ZlbWFsZScsIm1hbGUiKSx0cmVhdG1lbnQ9YygiaGlnaCIsImxvdyIpKQ0KcmVmY29uZGl0aW9uPC1saXN0KGMoImNvdmFyaWF0ZU5hbWUiLCJjb25kaXRpb25OYW1lIiksYygiY292YXJpYXRlTmFtZSIsImNvbmRpdGlvbk5hbWUiKSkgI2NvbmRpdGlvbiB3aXRoIHJlc3BlY3QgdG8gd2hpY2ggREUgb2Ygb3RoZXIgc2FtcGxlcyBpcyBjYWxjdWxhdGVkDQojIyMgZXhhbXBsZSBvZiBsb2FkaW5nIHRyYW5zY3JpcHQgdG8gZ2VuZSBtYXRyeCBmcm9tIGVuc2VtYmwgdXNpbmcgYmlvbWFSdDsgYWx0ZXJuYXRpdmVseSB5b3UgY2FuIHByb3ZpZGUgYSBtYXRyaXggd2l0aCB0aGUgZmlyc3QgY29sdW1uICh0YXJnZXRfaWQpIGNvbnRhaW5pbmcgdHJhbnNjcmlwdCBpZCBhbmQgc2Vjb25kIGNvbHVtbiAoZW5zX2dlbmUpIGNvbnRhaW5pbmcgZ2VuZSBpZDsgdGhpcyBzdGVwIGFnZ3JlZ2F0ZXMgdHJhbnNjcmlwdGlvbi1sZXZlbCBhbGlnbm1lbnQgdG8gZ2VuZS1sZXZlbCBjb3VudHMgIyMjDQplbnNlbWJsIDwtIHVzZU1hcnQoIkVOU0VNQkxfTUFSVF9FTlNFTUJMIixkYXRhc2V0PSJtbXVzY3VsdXNfZ2VuZV9lbnNlbWJsIikNCnR0ZzwtYmlvbWFSdDo6Z2V0Qk0oYXR0cmlidXRlcyA9IGMoImVuc2VtYmxfdHJhbnNjcmlwdF9pZCIsImVuc2VtYmxfZ2VuZV9pZCIpLG1hcnQgPSBlbnNlbWJsKQ0KdHRnIDwtIGRwbHlyOjpyZW5hbWUodHRnLCB0YXJnZXRfaWQgPSBlbnNlbWJsX3RyYW5zY3JpcHRfaWQsZW5zX2dlbmUgPSBlbnNlbWJsX2dlbmVfaWQpDQoNCmBgYA0KDQpPcHRpb25hbCBhZGp1c3RtZW50cyB0byBkZWZhdWx0IHBhcmFtZXRlcnMNCmBgYHtyLGV2YWw9cnVuUGlwZWxpbmV9DQphbGlnbm1ldGhvZDwtInNhbG1vbiIgI25hbWUgb2YgdGhlIGFsaWduZXINCg0KIyMgc2FsbW9uICMjDQprbWVyX2xlbmd0aD0zMQ0KbGliVHlwZT0nQScNCg0KIyMgREUgIyMNCmxvd2NvdW50ZmlsdGVyPVRSVUUgI2lmIGZpbHRlciBvdXQgbG93LWNvdW50IGdlbmVzIGJ5IENQTSB0aHJlc2hvbGQNCnRocmVzaENQTV9ieUF2Z0NvbFN1bT0xLjUvMTBlNiAjZ2VuZXMgcGFzc2luZyBmaWx0ZXIgaGF2ZSBhdmVyYWdlcyBvZiByZXBsaWNhdGVzID4gYXZnQ29sc3VtKnRocmVzaENQTV9ieUF2Z0NvbFN1bTsgYXZnQ29sU3VtID0gYXZlcmFnZSBvZiBzdW1zIG9mIGNvdW50cyBvZiBhbGwgc2FtcGxlczsgZ2VuZXMgcGFzc2luZyBmaWx0ZXIgZm9yIGVhY2ggYmF0Y2ggYXJlIGNhbGN1bGF0ZWQgc2VwYXJhdGVseQ0KbWF4TkRFRzwtJ2FsbCcgI21heGltdW0gbnVtYmVyIG9mIGdlbmVzIGluIHRoZSBvdXRwdXQgbWF0cml4DQpGRFI8LTEgI21heGltdW0gZmFsc2UgZGlzY292ZXJ5IHJhdGUgYWJvdmUgd2hpY2ggZ2VuZXMgd2lsbCBub3QgYmUgaW5jbHVkZWQgaW4gb3V0cHV0DQptdWx0aXBsZXRlc3RpbmdfbWV0aG9kPC0nQkgnICNtZXRob2QgdG8gYWRqdXN0IGZvciBtdWx0aXBsZSB0ZXN0aW5nOyBjaG9vc2UgZnJvbSAibm9uZSIsICJCSCIsICJCWSIgYW5kICJob2xtIg0KDQoNCmBgYA0KDQpJbml0aWFsaXppbmcgYW5hbHlzaXMuLi4NCmBgYHtyLCBldmFsPXJ1blBpcGVsaW5lfQ0KIyMgY2hlY2tpbmcgb3V0cHV0IGRpcmVjdG9yeSAjIw0KaWYoIWRpci5leGlzdHMob3V0RGlyKSl7ZGlyLmNyZWF0ZShvdXREaXIpfQ0KDQpiYXRjaGVzPC1jKCkNCmZvcihmcWkgaW4gZGF0YURpcl9mcSl7DQogIGZxaV9uYW1lPWJhc2VuYW1lKGZxaSkNCiAgYmF0Y2hlczwtYyhiYXRjaGVzLGZxaV9uYW1lKQ0KfQ0KY29udHJhc3RzPC1jb25kaXRpb25bLXdoaWNoKGNvbmRpdGlvbiAlaW4lIGMocmVmY29uZGl0aW9uKSldICNUT0RPOiBtb2RpZnkgdGhpcyB0byBnZW5lcmF0ZSBhIGxpc3Qgd2l0aCBsZW5ndGg9bGVuZ3RoKHJlZmNvbmRpdGlvbikNCg0KYGBgDQoNCg0KIyMjUUMgd2l0aCBmYXN0UUMNCmBgYHtyLCBldmFsPShGQUxTRSB8IHJ1blBpcGVsaW5lKX0NCmZvcihmcWkgaW4gZGF0YURpcl9mcSl7DQogIGZxaV9uYW1lPWJhc2VuYW1lKGZxaSkNCiAgZmFzdHFjX291dD1maWxlLnBhdGgob3V0RGlyLCJmYXN0UUNfcmVzdWx0cyIsZnFpX25hbWUpDQogIGRpci5jcmVhdGUoZmFzdHFjX291dCkNCiAgDQogIGZvcihmcSBpbiBsaXN0X2ZpbGVzX3dpdGhfZXh0cyhmcWksZmFzdHFfZXh0ZW5zaW9uLHJlY3Vyc2l2ZT1UUlVFLGZ1bGwubmFtZXMgPSBUUlVFKSl7DQogICAgc3lzdGVtKHBhc3RlMCgiZmFzdHFjIC1mIGZhc3RxICIsZnEsIiAtLWV4dHJhY3QgIiwiLS1vdXRkaXI9IixmYXN0cWNfb3V0KSkNCiAgfQ0KfQ0KYGBgDQoNCiMjI1BzZXVkby1hbGlnbm1lbnQgd2l0aCBzYWxtb24NCmBgYHtyLCBldmFsPShGQUxTRSB8IHJ1blBpcGVsaW5lKX0NCg0Kc2V0d2Qob3V0RGlyKQ0KaWYoaXMubmEoc2FsbW9uX2luZGV4KSl7DQogIHN5c3RlbShwYXN0ZTAoInNhbG1vbiBpbmRleCAtdCAiLHJlZmVyZW5jZV9mYSwiIC1pIHRyYW5zY3JpcHRzX2luZGV4X3NhbG1vbiAtLXR5cGUgcXVhc2kgLWsgMzEiKSkNCiAgc2FsbW9uX2luZGV4PSJ0cmFuc2NyaXB0c19pbmRleF9zYWxtb24iDQp9DQoNCmlmKCFwYWlyZWRfZW5kKXsNCiAgZm9yKGZxaSBpbiBkYXRhRGlyX2ZxKXsNCiAgICBmcWlfbmFtZT1iYXNlbmFtZShmcWkpDQogICAgc2FsbW9uX291dD1maWxlLnBhdGgob3V0RGlyLHBhc3RlMCgic2FsbW9uIixyZWZ0eHB0LGZxaV9uYW1lKSkNCiAgICBkaXIuY3JlYXRlKHNhbG1vbl9vdXQpDQogICAgZm9yKGZxIGluIGxpc3RfZmlsZXNfd2l0aF9leHRzKGZxaSxmYXN0cV9leHRlbnNpb24scmVjdXJzaXZlPVRSVUUsZnVsbC5uYW1lcyA9IFRSVUUpKXsNCiAgICAgIHNhbG1vbl9vdXRfaT1maWxlLnBhdGgoc2FsbW9uX291dCxmaWxlX3BhdGhfc2Fuc19leHQoYmFzZW5hbWUoZnEpKSkNCiAgICAgIGRpci5jcmVhdGUoc2FsbW9uX291dF9pKQ0KICAgICAgc3lzdGVtKHBhc3RlMCgic2FsbW9uIHF1YW50IC1pICIsc2FsbW9uX2luZGV4LCIgLWwgIixsaWJUeXBlLCIgLTEgIixmcSwiIC1vICIsc2FsbW9uX291dF9pKSkNCiAgICB9DQogIH0NCn1lbHNlew0KICBpZighaXMubmEobGliSURfbGVuKSl7DQogICAgZ2V0TGliUGFpcnMobGliSURfbGVuLGRhdGFEaXJfZnEpICNUT0RPOiBpbXBsZW1lbnQgZ2V0TGliUGFpcnMgaW4gdXRpbF9hbGlnbg0KICB9DQogIGZvcihmcXBhaXIgaW4gbGliUGFpcnMpew0KICAgIGZxcGFpcl9uYW1lPWJhc2VuYW1lKGRpcm5hbWUoZnFwYWlyW1sxXV0pKQ0KICAgIHNhbG1vbl9vdXQ9ZmlsZS5wYXRoKG91dERpcixwYXN0ZTAoInNhbG1vbiIscmVmdHhwdCxmcXBhaXJfbmFtZSkpDQogICAgaWYoIWRpci5leGlzdHMoc2FsbW9uX291dCkpe2Rpci5jcmVhdGUoc2FsbW9uX291dCl9DQogICAgc2FsbW9uX291dF9pPWZpbGUucGF0aChzYWxtb25fb3V0LGZpbGVfcGF0aF9zYW5zX2V4dChiYXNlbmFtZShmcXBhaXJbWzFdXSkpKQ0KICAgIGRpci5jcmVhdGUoc2FsbW9uX291dF9pKQ0KICAgIHN5c3RlbShwYXN0ZTAoInNhbG1vbiBxdWFudCAtaSAiLHNhbG1vbl9pbmRleCwiIC1sICIsbGliVHlwZSwiIC0xICIsZnFwYWlyW1sxXV0sIiAtMiAiLGZxcGFpcltbMl1dLCIgLW8gIixzYWxtb25fb3V0X2kpKQ0KICB9DQp9DQoNCmBgYA0KDQojIyNEaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcw0KYGBge3IsZXZhbD0oRkFMU0UgfCBydW5QaXBlbGluZSl9DQoNCiNUT0RPOiBtb2RpZnkgdXRpbF9kZS5SLCBpbmNsdWRlIG11bHRpcGxlIGNvdmFyaWF0ZXMgJiByZWZjb25kaXRpb24NCg0KDQpzb3VyY2VkaXI8LW91dERpcg0Kc2V0d2Qoc291cmNlZGlyKQ0KDQojI2dldCBjb3VudHMNCmNvdW50c2RmPC1jb3VudGRmKGFsaWdubWV0aG9kLHJlZnR4cHQsYmF0Y2hlcyxjb25kaXRpb24pDQoNCiMjbG93IGNvdW50IGZpbHRlcmluZyMjDQppZihsb3djb3VudGZpbHRlcj09VFJVRSl7DQogIGNvdW50c2RmQ1BNPC1maWx0ZXJDUE0oY291bnRzZGYsdGhyZXNoQ1BNX2J5QXZnQ29sU3VtKQ0KfQ0KDQpzYXZlcGF0aDwtZmlsZS5wYXRoKG91dERpciwnZGUnKQ0KaWYoIWRpci5leGlzdHMoc2F2ZXBhdGgpKXtkaXIuY3JlYXRlKHNhdmVwYXRoKX0NCg0KZm9yKGMgaW4gY29udHJhc3RzKXsNCiAgZGVyZXN1bHRzPC1kZShjb3VudHNkZkNQTV9waG90bzJfbm8yMG1XLGMsbXVsdGlwbGV0ZXN0aW5nX21ldGhvZCxuREVHID0gbWF4TkRFRyxGRFIgPSBGRFIpDQogIA0KICAjIGRyYXd2ZW5uKGRlcmVzdWx0cywiL1VzZXJzL1hpbnlpL3JuYS1zZXEvZGF0YS9uMmFfcHJlbGltL2RlL3Zlbm5fZGUiLHJlZmNvbmRpdGlvbixjKQ0KICBnZW5lbGlzdChkZXJlc3VsdHMsc2F2ZXBhdGgscGFzdGUwKGMpKQ0KfQ0KDQpgYGANCg0KI0JlbmNobWFyayBvZiBwaXBlbG5lICYgYWx0ZXJuYXRpdmUgY2hvaWNlZXMNCiMjKFBzZXVkby0pYWxpZ25tZW50IGFuZCBRdWFudGlmaWNhdGlvbg0KDQpUaGUgZmlyc3Qgc3RlcCBpbiB0aGUgYW5hbHlzaXMgaXMgdXN1YWxseSB0byB0cmFuc2Zvcm0gdGhlIGZhc3RxIGZpbGVzIGludG8gZ2VuZSBleHByZXNzaW9uIG1hdHJpY2VzLiBUaGlzIGludm9sdmVzIGFsaWduaW5nIHJlYWRzIHRvIHJlZmVyZW5jZSBnZW5vbWUgb3IgcmVmZXJlbmNlIHRyYW5zY3JpcHRvbWUgYW5kIHF1YW50aWZpY2F0aW9uIG9mIGFsaWduZWQgcmVhZHMuIEhlcmUncyBhIGxpc3Qgb2YgY2hvaWNlcyB0byBjb25zaWRlcjoNCg0KLSBjaG9pY2Ugb2YgcmVmZXJlbmNlDQogICAgKiBUaGUgY2hvaWNlIG9mIHJlZmVyZW5jZSB0cmFuc2NyaXB0b21lcyBoYXMgcHJvbm91bmNlZCBpbmZsdWVuY2Ugb24gZ2VuZSBxdWFudGlmaWNhdGlvbnMgZHVlIHRvIHJlYXNvbnMgc3VjaCBhcyBkaWZmZXJlbnQgZ2VuZSBkZWZpbml0aW9ucyBbQHpoYW9fY29tcHJlaGVuc2l2ZV9ub2RhdGVdLiBTZWUgW291ciBkZW1vbnN0cmF0aW9uIG9mIHRoZSBkaWZmZXJlbmNlIGluIERFIHJlc3VsdHMgZHVlIHRvIGNob2ljZSBvZiByZWZlcmVuY2VzIF0oI3JlZl9hbGlnbl9kZWFncmVlbWVudCkuDQogICAgKiBhbGlnbm1lbnQgdG8gZ2Vub21lIHZzIHRyYW5zY3JpcHRvbWUNCg0KLSBjaG9pY2Ugb2YgYWxpZ25tZW50L3BzZXVkby1hbGlnbm1lbnQgbWV0aG9kDQoNCiMjI3NpbXVsYXRpb24gYW5kIGJlbmNobWFyaw0KV2UgdXNlZCBQb2x5ZXN0ZXIgW0BmcmF6ZWVfcG9seWVzdGVyXzIwMTVdIHRvIHNpbXVsYXRlIFJOQXNlcSBsaWJyYXJpZXMgd2hlcmUgYSB1c2VyIGdlbmVyYXRlZCBmb2xkLWNoYW5nZSBtYXRyaXggaXMgdXNlZCBhcyB0aGUgZ3JvdW5kIHRydXRoLiBUaGlzIHNpbXVsYXRpb24gaXMgYmFzZWQgb24gbW91c2UgbVJOQSByZWZlcmVuY2UgdHJhbnNjcmlwdG9tZSBmcm9tIFVDU0MgKHNpbUFsaWduL3JlZnR4bXJuYV91Y3NjX3JlZmdlbmUuZmFzdGEuZ3osIGFzc2VtYmx5IEdSQ20zOC9tbTEwLCBVQ1NDIFJlZlNlcSAocmVmR2VuZSkgdGFibGUgW0BrYXJvbGNoaWtfdWNzY18yMDA0XSkuDQoNClRvIHVzZSBvdXIgc2ltdWxhdGVkIGRhdGEsIHBsZWFzZSByZWZlciB0byBzaW1BbGlnbi9zaW1mYXN0YTEsIHNpbUFsaWduL3NpbWZhc3RhMiwgc2ltQWxpZ24vc2ltZmFzdGEzDQoNClRvIHVzZSBvdXIgZnJhbWV3b3JrIGRpcmVjdGx5LCBwbGVhc2UgcmVmZXIgdG8gdGhlIGNvZGUgYmVsb3cgYW5kIHJlcGxhY2UgcGFyYW1ldGVyIHZhbHVlcyBhcyBuZWVkZWQgKHNldCBldmFsIG9yIHJ1blNpbXVsYXRpb24gdG8gVFJVRSBiZWZvcmUgcnVubmluZykuDQoNCiMjIyNSdW4gc2ltdWxhdGlvbg0KUmVxdWlyZWQgcGFja2FnZXM6DQotIFBvbHllc3RlciBbQGZyYXplZV9wb2x5ZXN0ZXJfMjAxNV0NCi0gc2VxaW5yDQotIEJpb3N0cmluZ3MNCmBgYHtyLCBldmFsPShGQUxTRSB8IHJ1blNpbXVsYXRpb24pfQ0KcmVmUGF0aDwtInJlZnR4L3JlZnR4bXJuYV91Y3NjX3JlZmdlbmUuZmFzdGEuZ3oiICNwYXRoIHRvIHJlZmVyZW5jZSB0cmFuc2NyaXB0b21lDQpuZ3JvdXA8LTIgI251bWJlciBvZiBjb25kaXRpb25zIGluIHRoZSBsaWJyYXJ5DQpuZGF0YTwtMyAjbnVtYmVyIG9mIHJlcGxpY2F0ZXMgaW4gZWFjaCBjb25kaXRpb24NCmRlcHJvYjwtMC4xNSAjcHJvYmFiaWxpdHkgb2YgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24NCmRlbWVhbjwtMiAjbWVhbiBvZiBmb2xkIGNoYW5nZSBmb3IgREUgZ2VuZXMNCnNhdmVwYXRoX2ZjbXR4PSdzaW1mY210eCcgI2RpcmVjdG9yeSB0byBzYXZlIGZvbGQgY2hhbmdlIG1hdHJpeA0Kc2F2ZXBhdGhfbGliPSJzaW1mYXN0YSIgI2RpcmVjdG9yeSB0byBzYXZlIHNpbXVsYXRlZCBsaWJyYXJpZXMNCnNpbXVsYXRlTGliX3BvbHllc3RlcihyZWZQYXRoLG5ncm91cCxuZGF0YSxkZXByb2Isc2F2ZXBhdGhfZmNtdHgsc2F2ZXBhdGhfbGliKQ0KYGBgDQoNCiMjIyNDb21wYXJpc29ucyBvZiBtYXBwaW5nIHJhdGVzDQpNYXBwaW5nIHJhdGVzIGJldHdlZW4gU2FsbW9uIGFuZCBrYWxsaXN0byBhcmUgY29tcGFyaWJsZSB3aGVuIHVzaW5nIGVuc2VtYmxlIGFuZCBVQ1NDIGFzIHRoZSByZWZlcmVuY2VzLk1hcHBpbmcgcmF0ZXMgY2FuIGJlIGZvdW5kIGFzIG91dHB1dHMgb2YgU2FsbW9uIGFuZCBLYWxsaXN0bw0KDQohW10oc2ltQWxpZ24vcmVzdWx0cy9NYXBwaW5nUmF0ZXMuanBlZyl7IHdpZHRoPTcwJSB9DQoNCiMjIyNDb21wYXJpc29ucyBvZiBxdWFudGlmaWNhdGlvbiBlcnJvcnMNCg0KcGVyY2VudCBlcnJvciBvZiB0cmFuc2NyaXB0IFggPSBhYnMoZXN0aW1hdGVkIHRyYW5zY3JpcHQgY291bnQgb2YgWCAtIGFjdHVhbCBjb3VudCBvZiBYKS9hY3R1YWwgY291bnQgb2YgWCAqIDEwMA0KIVtdKHNpbUFsaWduL3Jlc3VsdHMvcXVhbnRFcnJvci5wbmcpeyB3aWR0aD03MCUgfQ0KDQojIyMjSW1wYWN0cyBvZiByZWZlcmVuY2UgYW5kIChwc2V1ZG8tKWFsaWdubWVudCBtZXRob2RzIG9uIGRvd25zdHJlYW0gREUgYW5hbHlzaXMgeyNyZWZfYWxpZ25fZGVhZ3JlZW1lbnR9DQpXZSBjb21wYXJlZCB0aGUgaW1wYWN0cyBvbiB0d28gREUgYWxnb3JpdGhtczogREVzZXEyIGFuZCBsaW1tYS12b29tLiBUbyByZXByb2R1Y2Ugb3VyIHJlc3VsdHMsIHJ1biBkZUFncmVlbWVudC5SIChyZXN1bHRzIHdpbGwgYmUgc3RvcmVkIGluIHNpbUFsaWduL3Jlc3VsdHMvZGVhZ3JlZW1lbnQpLiBUaGlzIGRldGVjdHMgREUgYmV0d2VlbiB0d28gdGltZSBwb2ludHMgaW4gb3VyIGFscGhhLXN5biBleHBlcmltZW50IChUMiBhbmQgVDcpLg0KDQpUd28gZXhhbXBsZXMgdXNpbmcgREVzZXEyIGFuZCB2b29tIGFyZSBzaG93biBiZWxvdy4NCg0KIVtdKHNpbUFsaWduL3Jlc3VsdHMvZGVhZ3JlZW1lbnQvY29tcHJlc3NlZEV4YW1wbGVzL3B2YWx1ZTA1QmF0Y2gxZGVzZXEyLmpwZyl7IHdpZHRoPTcwJSB9DQohW10oc2ltQWxpZ24vcmVzdWx0cy9kZWFncmVlbWVudC9jb21wcmVzc2VkRXhhbXBsZXMvcHZhbHVlMDVCYXRjaDF2b29tLmpwZyl7IHdpZHRoPTcwJSB9DQoNCg0KIyNOb3JtYWxpemF0aW9uIGFuZCBERSBhbmFseXNpcw0KU3RhdGlzdGljYWwgaW5mZXJlbmNlcyBvZiBkaWZmZXJlbnRpYWwgZ2VuZSBleHByZXNzaW9uIChERSksIGluY2x1ZGluZyB0aGUgbm9ybWFsaXphdGlvbiBwcm9jZWR1cmVzLCB1c3VhbGx5IG1ha2UgYXNzdW1wdGlvbnMgYWJvdXQgdHlwaWNhbCBkYXRhc2V0cycgY2hhcmFjdGVyaXN0aWNzLCBzdWNoIGFzIGdlbmUtY291bnQgZGlzdHJpYnV0aW9uIGFuZCBkZWdyZWUgb2YgREUgW0BzZXllZG5hc3JvbGxhaF9jb21wYXJpc29uXzIwMTUsIEBkaWxsaWVzX2NvbXByZWhlbnNpdmVfMjAxMywgQGRpbGxpZXNfY29tcHJlaGVuc2l2ZV8yMDEzXS4gSXQgaXMgaW1wb3J0YW50IHRvIHVuZGVyc3RhbmQgaG93IHJvYnVzdCBlYWNoIG5vcm1hbGl6YXRpb24gb3IgREdFIGFuYWx5c2lzIG1ldGhvZCBpcyB3aGVuIGl0cyBhc3N1bXB0aW9ucyBhcmUgdmlvbGF0ZWQuDQoNClRvIHRlc3QgdGhlIHBlcmZvcm1hbmNlIGdpdmVuIGRhdGFzZXRzIHdpdGggZGlzdGluY3QgZmVhdHVyZXMsIHdlIHNpbXVsYXRlZCANCg0KLSAyMyBkYXRhc2V0cyB3aXRoIDExIHZhcnlpbmcgcGFyYW1ldGVycw0KLSAxMSBlbXBpcmljYWxseSBzaW11bGF0ZWQgZGF0YXNldHMgd2l0aCA1IHZhcnlpbmcgcGFyYW1ldGVycw0KDQohW10odGVzdGRhdGEvcGFyYW1ldGVycy5wbmcpeyB3aWR0aD03MCUgfQ0KDQpBbGwgc2ltdWxhdGlvbnMgdXNlZCBTcGxhdHRlciBbQHphcHBpYV9zcGxhdHRlcjpfMjAxN10gd2l0aCBkcm9wb3V0IHJhdGUgZXF1YWxzIHRvIHplcm8uIEZpeGVkIHBhcmFtZXRlcnMgb2YgdGhlIGVtcGlyaWNhbCBzaW11bGF0aW9ucyB3ZXJlIGRlcml2ZWQgZnJvbSBhIGRhdGFzZXQgYnkgQm90dG9tbHkgZXQgYWwgW0Bib3R0b21seV9ldmFsdWF0aW5nXzIwMTFdLiANCg0KIyMjU2ltdWxhdGlvbiByZXN1bHRzDQpXZSB0ZXN0ZWQgdGhlIGZvbGxvd2luZyBjb21iaW5hdGlvbnMgb2Ygbm9ybWFsaXphdGlvbiBhbmQgREUgYW5hbHlzaXMgbWV0aG9kczoNCg0KLSBlZGdlUiB3aXRoDQogICAgLSBUUE0NCiAgICAtIEZQS00NCiAgICAtICpUcmltbWVkIE1lYW4gb2YgTS12YWx1ZXMgKFRNTSksIGRlZmF1bHQgbm9ybWFsaXphdGlvbiBvZiBlZGdlUioNCiAgICAtIFJlbGF0aXZlIExvZyBFeHByZXNzaW9uIChSTEUpDQogICAgLSBVcHBlciBRdWFydGlsZSAoVVEpDQogICAgLSBSVVZnICsgVE1NDQogICAgLSBSVVZnIChyZW1vdmUgdW53YW50ZWQgdmFyaWF0aW9uKSArIFJMRQ0KICAgIC0gUlVWZyArIFVRDQogICAgLSBERUdFUyAoREVHIGVsaW1pbmF0aW9uIHN0cmF0ZWd5KSArIFRNTQ0KICAgIC0gREVHRVMgKyBSTEUsIERFR0VTICsgVGJUDQogICAgDQotIERFR0VTIHdpdGgNCiAgICAtIERFU2VxMg0KICAgIC0gYmF5U2VxDQogICAgLSBlZGdlUg0KICAgIC0gbGltbWEtdm9vbSANCiAgICANCi0gYmF5U2VxLCBERVNlcTIsIGFuZCBsaW1tYS12b29tIHdpdGggdGhlaXIgZGVmYXVsdCBub3JtYWxpemF0aW9uIG1ldGhvZHMNCg0KVGhlIGJlbmNobWFyayByZXN1bHRzIGNhbiBiZSBmb3VuZCBpbiB0ZXN0ZGF0YS9ub3JtdGVzdHJlc3VsdHMgYW5kIHRlc3RkYXRhL0RFdGVzdHJlc3VsdHMgIA0KVGhlIHNpbXVsYXRlZCBkYXRhc2V0cyBjYW4gYmUgZm91bmQgaW4gdGVzdGRhdGEvc2ltdWxhdGVkRGF0YSAgDQpUbyByZXByb2R1Y2Ugb3VyIHJlc3VsdHMsIHNlZSB0aGUgW25leHQgc2VjdGlvbl0oI2RlX3NpbXRlc3QpICANCg0KQ2hvaWNlIG9mIG5vcm1hbGl6YXRpb24gbWV0aG9kcyBoYWQgdmVyeSBzbWFsbCBpbXBhY3Qgb24gdGhlIGJlbmNobWFya2luZyByZXN1bHRzLiBUaGUgUk9DLCBhY2N1cmFjeSB2cyBGRFIsIGFuZCBGRFIgY29udHJvbCBjdXJ2ZXMgYXJlIGNvbXBhcmFibGUgZm9yIGFsbCBub3JtYWxpemF0aW9ucy4gQW4gZXhhbXBsZSBpcyBzaG93biBmb3IgUk9DDQoNCiFbXSh0ZXN0ZGF0YS9ub3JtdGVzdHJlc3VsdHMvdmlzdWFsaXplZHJlc3VsdHMvcm9jL2VtcHNpbV9kZS5kb3duUHJvYl9kZWYuanBlZyl7IHdpZHRoPTcwJSB9DQoNCkNob2ljZSBvZiBERSBhbmFseXNpcyBtZXRob2RzIGhhcyB0cmFkZS1vZmYgYmV0d2VlbiB0cnVlIHBvc2l0aXZlIHJhdGVzIGFuZCBmYWxzZSBkaXNjb3ZlcnkgcmF0ZXMNCg0KIVtdKHRlc3RkYXRhL0RFdGVzdHJlc3VsdHMvdmlzdWFsaXplZGRlcmVzdWx0cy90cHJ2YWx1ZS9lbXBzaW1fZGUuZG93blByb2JfZGVmLmpwZWcpeyB3aWR0aD03MCUgfQ0KDQohW10odGVzdGRhdGEvREV0ZXN0cmVzdWx0cy92aXN1YWxpemVkZGVyZXN1bHRzL3RydWVGRFIvZW1wc2ltX2RlLmRvd25Qcm9iX2RlZi5qcGVnKXsgd2lkdGg9NzAlIH0NCg0KIyMjUnVuIHNpbXVsYXRpb24gJiBiZW5jaG1hcmsgeyNkZV9zaW10ZXN0fQ0KUmVxdWlyZXM6DQotIHNwbGF0dGVyIDEuMC4zIFtAemFwcGlhX3NwbGF0dGVyOl8yMDE3XQ0KDQpgYGB7ciwgZXZhbD0oRkFMU0UgfCBydW5TaW11bGF0aW9uKX0NCiMjIGVudGVyIHRoZSBwYXJhbWV0ZXIgdmFsdWVzIHlvdSB3YW50IHRvIHRlc3QgIyMNCmdyb3VwQ2VsbHM8LWMoMyw5LDYpDQptZWFuLnNoYXBlPC1jKDAuNCwwLjgsMC42KQ0KbWVhbi5yYXRlPC1jKDAuMSwwLjUsMC4zKQ0KbGliLmxvYzwtIGMoNiwxNiwxMSkNCmxpYi5zY2FsZTwtYygwLjE1LDAuNSwwLjM1KQ0Kb3V0LnByb2I8LWMoMC4wMSwwLjEsMC4wNSkNCmRlLnByb2I8LWMoMC4wNSwwLjMsMC4xKQ0KZGUuZG93blByb2I8LWMoMC4yNSwwLjc1LDAuNSkNCmRlLmZhY0xvYzwtYygwLjA1LDAuMywwLjEpDQpkZS5mYWNTY2FsZTwtYygwLjIsMC42LDAuNCkNCmJjdi5jb21tb248LWMoMC4wNSwwLjIsMC4xKQ0KDQpnZW5lcmF0ZVNpbXVsYXRlZENvdW50cyhub3JtYWxpemF0aW9uTWV0aG9kLGRlTWV0aG9kLGdyb3VwQ2VsbHMsbWVhbi5zaGFwZSxtZWFuLnJhdGUsbGliLmxvYyxsaWIuc2NhbGUsb3V0LnByb2IsZGUucHJvYixkZS5kb3duUHJvYixkZS5mYWNMb2MsYmN2LmNvbW1vbikNCmBgYA0KDQoNCiNJbnRlcnByZXRhdGlvbiBhbmQgdmFsaWRhdGlvbiBvZiBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcyB7I2FmdGVyREV9DQojI0ZhbHNlIHBvc2l0aXZlcw0KV2UgcGVyZm9ybWVkIFJOQS1zZXEgdHdpY2Ugb24gYSB0b3RhbCBvZiA2IHNhbXBsZXMgb2YgbW91c2UgTmV1cm8yQSBjZWxsIGN1bHR1cmVzICgzIHNhbXBsZXMgZm9yIGVhY2ggUk5BLXNlcSkuIFRoZSBzYW1lIHByb3RvY29scyB3ZXJlIHVzZWQgYW5kIGFsbCBsaWJyYXJpZXMgaGF2ZSBjb21wYXJhYmxlIHF1YWxpdHkuIFdlIGZvdW5kIDI2NjUgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzIHdpdGggRkRSIDwgMC4wNSB1c2luZyB0aGUgZGVmYXVsdCBzZXR0aW5ncyBvZiB0aGlzIHBpcGVsaW5lLiBXaXRoIHRoZSBsaW1pdGVkIG51bWJlciBvZiByZXBsaWNhdGVzIGF2YWlsYWJsZSBpbiBhIHR5cGljYWwgUk5BLXNlcSBleHBlcmltZW50LCBpdCBpcyBpbXBvcnRhbnQgdG8gY29uc2lkZXIgY29uZm91bmRpbmcgZGlmZmVyZW5jZXMgY2F1c2VkIGJ5IGV4cGVyaW1lbnRhbCBkZXNpZ24gYW5kIGhhbmRsaW5nLiBJbiB0aGUgZm9sbG93aW5nIHNlY3Rpb25zLCB3ZSBwcm92aWRlIHNvbWUgbWV0aG9kcyBmb3IgdmFsaWRhdGlvbiBhbmQgaW50ZXJwcmV0YXRpb24gb2YgREUgZ2VuZXMuDQoNCiMjRnVuY3Rpb25hbCBlbnJpY2htZW50IGFuYWx5c2lzDQojIyNTdGF0aXN0aWNhbCBvdmVyLXJlcHJlc2VudGF0aW9uIHRlc3QNClRoaXMgdGVzdHMgZm9yIGFueSBvdmVyIG9yIHVuZGVyIHJlcHJlc2VudGVkIGFubm90YXRpb24gdGVybXMgaW4gdGhlIHF1ZXJ5IGxpc3Qgd2l0aCByZXNwZWN0IHRvIHRoZSBiYWNrZ3JvdW5kLiBTZXZlcmFsIG9wdGlvbnMgYXJlIGxpc3RlZCBiZWxvdzogIA0KDQoqIERBVklEIFtAaHVhbmdfc3lzdGVtYXRpY18yMDA5XTogaHR0cHM6Ly9kYXZpZC5uY2lmY3JmLmdvdi8NCiAgICAtIE9ubHkgb3Zlci1yZXByZXNlbnRhdGlvbnMgYXJlIGNhbGN1bGF0ZWQgIA0KICAgIC0gTGFzdCBrbm93bGVkZ2UgYmFzZSB1cGRhdGUgd2FzIGluIE1heSAyMDE2ICANCiAgICAtIERBVklEIGhhcyBhY2Nlc3MgdG8gbXVsdGlwbGUgZGF0YWJhc2VzICANCiAgDQoqIFBBTlRIRVIgW0BtaV9wcm90b2NvbF8yMDE5XTogaHR0cDovL3BhbnRoZXJkYi5vcmcvICANCiAgICAtIEJvdGggb3ZlciBhbmQgdW5kZXIgcmVwcmVzZW50YXRpb25zIGFyZSBjYWxjdWxhdGVkICANCiAgICAtIE1haW50YWluZWQgdXAgdG8gZGF0ZSB3aXRoIEdlbmUgT250b2xvZ3kgYW5ub3RhdGlvbnMgIA0KICANCiMjIyNDaG9vc2luZyBiYWNrZ3JvdW5kIGdlbmUgbGlzdA0KU29tZSBjb21tb24gY2hvaWNlcyBvZiBiYWNrZ3JvdW5kIGluY2x1ZGUgZ2VuZXMga25vd24gdG8gYmUgZXhwcmVzc2VkIGluIGEgcGFydGljdWxhciBjZWxsIHR5cGUgb2YgaW50ZXJlc3QuIEEgcHJveHkgZm9yIGNlbGwtdHlwZSBzcGVjaWZpYyBiYWNrZ3JvdW5kIGNhbiBiZSBmb3VuZCBieSB1c2luZyBhbGwgbm9uLXplcm8gZ2VuZXM6DQpgYGB7cixldmFsPUZBTFNFfQ0KY291bnRzbXR4PC1hcy5tYXRyaXgoYXMuZGF0YS5mcmFtZShjb3VudHNkZikpDQpiYWNrZ3JvdW5kPC1yb3duYW1lcyhjb3VudHNtdHgpW3doaWNoKHJvd1N1bXMoY291bnRzbXR4KSA+IDApXQ0KYGBgDQpIb3dldmVyLCBpdCBpcyBwb3NzaWJsZSB0aGF0IHRoaXMgbGlzdCBkb2VzIG5vdCBjb3ZlciBhbGwgZ2VuZXMgdGhhdCBjYW4gYmUgZXhwcmVzc2VkIGR1ZSB0byB0aGUgbGltaXRlZCBleHBlcmltZW50YWwgY29uZGl0aW9ucyB0ZXN0ZWQuDQoNCiMjI0dTRUENCiMjI1dHQ05BDQojIyNBREFHRQ0KDQojI0RpbWVuc2lvbmFsaXR5IHJlZHVjdGlvbg0KDQojI0VucmljaG1lbnQgb2YgdHJhbnNjcmlwdGlvbiBmYWN0b3JzDQoNCiMjQmF0Y2ggY29uc2Vuc3VzDQoNCiMgUmVmZXJlbmNlcw0K